//! 字符串指令编码模块
//! 
//! 实现 8086 字符串指令：MOVS, CMPS, SCAS, LODS, STOS

use crate::ast::Operand;
use crate::error::{AssemblerError, Result};

/// 字符串指令编码器
pub struct StringEncoder;

impl StringEncoder {
    /// 编码字符串指令
    pub fn encode(&self, instruction: &str, operands: &[Operand]) -> Result<Vec<u8>> {
        match instruction {
            "移动字符串字节" => self.encode_movsb(operands),
            "移动字符串字" => self.encode_movsw(operands),
            "比较字符串字节" => self.encode_cmpsb(operands),
            "比较字符串字" => self.encode_cmpsw(operands),
            "扫描字符串字节" => self.encode_scasb(operands),
            "扫描字符串字" => self.encode_scasw(operands),
            "加载字符串字节" => self.encode_lodsb(operands),
            "加载字符串字" => self.encode_lodsw(operands),
            "存储字符串字节" => self.encode_stosb(operands),
            "存储字符串字" => self.encode_stosw(operands),
            _ => Err(AssemblerError::CodeGenError {
                message: format!("未知的字符串指令: {}", instruction),
            }),
        }
    }

    /// 编码移动字符串字节指令（MOVSB）
    /// 
    /// MOVSB: 从 [SI] 复制一个字节到 [DI]，然后根据DF调整SI和DI
    /// 操作码: 0xA4
    fn encode_movsb(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "移动字符串字节 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xA4])
    }

    /// 编码移动字符串字指令（MOVSW）
    /// 
    /// MOVSW: 从 [SI] 复制一个字到 [DI]，然后根据DF调整SI和DI
    /// 操作码: 0xA5
    fn encode_movsw(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "移动字符串字 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xA5])
    }

    /// 编码比较字符串字节指令（CMPSB）
    /// 
    /// CMPSB: 比较 [SI] 和 [DI] 的一个字节，然后根据DF调整SI和DI
    /// 操作码: 0xA6
    fn encode_cmpsb(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "比较字符串字节 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xA6])
    }

    /// 编码比较字符串字指令（CMPSW）
    /// 
    /// CMPSW: 比较 [SI] 和 [DI] 的一个字，然后根据DF调整SI和DI
    /// 操作码: 0xA7
    fn encode_cmpsw(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "比较字符串字 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xA7])
    }

    /// 编码扫描字符串字节指令（SCASB）
    /// 
    /// SCASB: 在 [DI] 中搜索 AL，然后根据DF调整DI
    /// 操作码: 0xAE
    fn encode_scasb(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "扫描字符串字节 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xAE])
    }

    /// 编码扫描字符串字指令（SCASW）
    /// 
    /// SCASW: 在 [DI] 中搜索 AX，然后根据DF调整DI
    /// 操作码: 0xAF
    fn encode_scasw(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "扫描字符串字 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xAF])
    }

    /// 编码加载字符串字节指令（LODSB）
    /// 
    /// LODSB: 从 [SI] 加载一个字节到 AL，然后根据DF调整SI
    /// 操作码: 0xAC
    fn encode_lodsb(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "加载字符串字节 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xAC])
    }

    /// 编码加载字符串字指令（LODSW）
    /// 
    /// LODSW: 从 [SI] 加载一个字到 AX，然后根据DF调整SI
    /// 操作码: 0xAD
    fn encode_lodsw(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "加载字符串字 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xAD])
    }

    /// 编码存储字符串字节指令（STOSB）
    /// 
    /// STOSB: 从 AL 存储一个字节到 [DI]，然后根据DF调整DI
    /// 操作码: 0xAA
    fn encode_stosb(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "存储字符串字节 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xAA])
    }

    /// 编码存储字符串字指令（STOSW）
    /// 
    /// STOSW: 从 AX 存储一个字到 [DI]，然后根据DF调整DI
    /// 操作码: 0xAB
    fn encode_stosw(&self, operands: &[Operand]) -> Result<Vec<u8>> {
        if !operands.is_empty() {
            return Err(AssemblerError::OperandError {
                message: "存储字符串字 指令不需要操作数".to_string(),
                line: 0,
            });
        }
        Ok(vec![0xAB])
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_encode_movsb() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("移动字符串字节", &operands).unwrap();
        // MOVSB = A4
        assert_eq!(result, vec![0xA4]);
    }

    #[test]
    fn test_encode_movsw() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("移动字符串字", &operands).unwrap();
        // MOVSW = A5
        assert_eq!(result, vec![0xA5]);
    }

    #[test]
    fn test_encode_cmpsb() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("比较字符串字节", &operands).unwrap();
        // CMPSB = A6
        assert_eq!(result, vec![0xA6]);
    }

    #[test]
    fn test_encode_cmpsw() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("比较字符串字", &operands).unwrap();
        // CMPSW = A7
        assert_eq!(result, vec![0xA7]);
    }

    #[test]
    fn test_encode_scasb() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("扫描字符串字节", &operands).unwrap();
        // SCASB = AE
        assert_eq!(result, vec![0xAE]);
    }

    #[test]
    fn test_encode_scasw() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("扫描字符串字", &operands).unwrap();
        // SCASW = AF
        assert_eq!(result, vec![0xAF]);
    }

    #[test]
    fn test_encode_lodsb() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("加载字符串字节", &operands).unwrap();
        // LODSB = AC
        assert_eq!(result, vec![0xAC]);
    }

    #[test]
    fn test_encode_lodsw() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("加载字符串字", &operands).unwrap();
        // LODSW = AD
        assert_eq!(result, vec![0xAD]);
    }

    #[test]
    fn test_encode_stosb() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("存储字符串字节", &operands).unwrap();
        // STOSB = AA
        assert_eq!(result, vec![0xAA]);
    }

    #[test]
    fn test_encode_stosw() {
        let encoder = StringEncoder;
        let operands = vec![];
        let result = encoder.encode("存储字符串字", &operands).unwrap();
        // STOSW = AB
        assert_eq!(result, vec![0xAB]);
    }

    #[test]
    fn test_encode_string_with_operands() {
        let encoder = StringEncoder;
        let operands = vec![Operand::Register("累加器".to_string())];
        // 所有字符串指令都不应该接受操作数
        assert!(encoder.encode("移动字符串字节", &operands).is_err());
        assert!(encoder.encode("移动字符串字", &operands).is_err());
        assert!(encoder.encode("比较字符串字节", &operands).is_err());
        assert!(encoder.encode("比较字符串字", &operands).is_err());
        assert!(encoder.encode("扫描字符串字节", &operands).is_err());
        assert!(encoder.encode("扫描字符串字", &operands).is_err());
        assert!(encoder.encode("加载字符串字节", &operands).is_err());
        assert!(encoder.encode("加载字符串字", &operands).is_err());
        assert!(encoder.encode("存储字符串字节", &operands).is_err());
        assert!(encoder.encode("存储字符串字", &operands).is_err());
    }
}

